/** @file vtparser.c
 *
 * @author Joshua Haberman <joshua@reverberate.org>
 * @author _modified by_ cameris <camerisdump@gmail.com>
 */

#include "vtparser.h"
#include <string.h>

/** @brief initialize a parser
 * @param parser the parser to initialize
 * @param cb set the callback function of the parser */
void vtparse_init(struct vtparse *parser,
		void (*cb)(struct vtparse *parser, uint8_t action , uint8_t ch))
{ /* {{{ */
    parser->state                 = VTPARSE_STATE_GROUND;
    parser->intermediate_chars[0] = '\0';
    parser->num_params            = 0;
    parser->ignore_flagged        = 0;
    parser->cb                    = cb;
} /* }}} */

/** @brief handles certain actions by the parser, the rest is given to the callback
 * @param parser the parser
 * @param action the action to perform
 * @param ch character parsed */
static void do_action(struct vtparse *parser, uint8_t action, uint8_t ch)
{ /* {{{ */
	switch(action) {
		case VTPARSE_ACTION_PRINT:
		case VTPARSE_ACTION_EXECUTE:
		case VTPARSE_ACTION_HOOK:
		case VTPARSE_ACTION_PUT:
		case VTPARSE_ACTION_OSC_START:
		case VTPARSE_ACTION_OSC_PUT:
		case VTPARSE_ACTION_OSC_END:
		case VTPARSE_ACTION_UNHOOK:
		case VTPARSE_ACTION_CSI_DISPATCH:
		case VTPARSE_ACTION_ESC_DISPATCH:
			parser->cb(parser, action, ch);
			break;

		case VTPARSE_ACTION_IGNORE:
			/* do nothing */
			break;

		case VTPARSE_ACTION_COLLECT: {
			/* Append the character to the intermediate params */
			uint8_t num_intermediate_chars = strlen((char*)parser->intermediate_chars);

			if(num_intermediate_chars + 1 > MAX_INTERMEDIATE_CHARS)
				parser->ignore_flagged = 1;
			else
				parser->intermediate_chars[num_intermediate_chars++] = ch;

			break;
		}

		case VTPARSE_ACTION_PARAM: {
			/* process the param character */
			if(ch == ';') {
				parser->num_params += 1;
				parser->params[parser->num_params-1] = 0;
			} else {
				/* the character is a digit */
				uint_fast8_t current_param;

				if(parser->num_params == 0) {
					parser->num_params = 1;
					parser->params[0]  = 0;
				}

				current_param = parser->num_params - 1;
				parser->params[current_param] *= 10;
				parser->params[current_param] += (ch - '0');
			}

			break;
		}

		case VTPARSE_ACTION_CLEAR:
			for (uint_fast8_t i=0; i< MAX_INTERMEDIATE_CHARS+1;i++) {
				parser->intermediate_chars[i] = '\0';
			}
			parser->num_params            = 0;
			parser->ignore_flagged        = 0;
			break;
	}
} /* }}} */

/** @brief performs a state change, also handles entry and exit actions of the states
 * @param parser the parser
 * @param change combined state and action
 * @param ch character parsed */
static void do_state_change(struct vtparse *parser, uint8_t change, uint8_t ch)
{ /* {{{ */
    /* A state change is an action and/or a new state to transition to. */
    enum vtparse_state  new_state = STATE(change);
    enum vtparse_action action    = ACTION(change);

	if (new_state) {
        /* Perform up to three actions:
         *   1. the exit action of the old state
         *   2. the action associated with the transition
         *   3. the entry action of the new action
         */
		enum vtparse_action exit_action = EXIT_ACTIONS[parser->state];
		enum vtparse_action entry_action = ENTRY_ACTIONS[new_state];

		if (exit_action)
			do_action(parser, exit_action, 0);

		if (action)
			do_action(parser, action, ch);

		if (entry_action)
			do_action(parser, entry_action, 0);

		parser->state = new_state;
	} else {
		do_action(parser, action, ch);
	}
} /* }}} */

/** @brief parse a character
 * @param parser the parser,
 * @param ch character to parse */
void vtparse(struct vtparse *parser, uint8_t ch)
{ /* {{{ */
	uint8_t change = (*STATE_TABLE[VTPARSE_STATE_ANYWHERE])(ch);

	if (!change)
		change = (*STATE_TABLE[parser->state])(ch);

	do_state_change(parser, change, ch);
} /* }}} */

/* vim:set foldmethod=marker: */
